﻿#include "CTPTime.h"
#include "Tools.h"
#include <fstream>
#include <QFile>
#include "OptionManager.h"
#include <QTextStream>

#define MarketDateTime_Morning_BeginHour 8 // 上午行情的起始时间
#define MarketDateTime_Morning_EndHour 12 // 上午行情的结束时间
#define MarketDateTime_Afternoon_BeginHour 12 // 下午行情段的起始时间
#define MarketDateTime_Afternoon_EndHour 16 // 下午行情段的结束时间
#define MarketDateTime_Evening_BeginHour 20 // 晚上行情段的起始时间
#define MarketDateTime_Evening_EndHour 4 // 晚上行情段的结束时间
#define NoMarketDateTime_Afternoon_BeginHour 16 // 下午的非行情段的起始时间
#define NoMarketDateTime_Afternoon_EndHour 20 // 下午的非行情段的结束时间
#define NoMarketDateTime_Evening_BeginHour 4  // 晚上的非行情段的起始时间
#define NoMarketDateTime_Evening_EndHour 8  // 晚上的非行情段的结束时间

int CTPTm::DateTimeSectionTag(const std::string& pDtStr)
{
	tm tm_ = Tools::DateTimeStr2TM(pDtStr);
	return CTPTm::DateTimeSectionTag(tm_);
}

void CTPTm::DateRangeToStringList(time_t pBeginDay, time_t pEndDay, std::vector<std::string> &outDayStr)
{
	std::vector<time_t> pDays;
	DateRangeToDays(pBeginDay, pEndDay, pDays);

	std::vector<std::string> dbNames;
	for (int i = 0; i < pDays.size(); ++i)
	{
		tm structDt = Tools::GetDateTimeFrmStamp(pDays.at(i));
		dbNames.emplace_back(Tools::DateToStr(structDt));
	}
	outDayStr.swap(dbNames);
}

void CTPTm::DateRangeToDays(time_t pBeginTime, time_t pEndTime, std::vector<std::pair<time_t, time_t>> &outDays)
{
	std::vector<time_t> dbNames;
	DateRangeToDays(pBeginTime, pEndTime, dbNames);
	if (dbNames.empty())
	{
		return;
	}
	if (dbNames.size() < 2)
	{
		outDays.emplace_back(std::make_pair(pBeginTime, pEndTime));
	}
	else
	{
		std::vector<std::pair<time_t, time_t>> pDays;
		for (int i = 0; i < dbNames.size(); ++i)
		{
			tm dayBegin = Tools::GetDateTimeFrmStamp(dbNames.at(i));
			tm dayEnd = dayBegin;
			dayEnd.tm_hour = 23;
			dayEnd.tm_min = 59;
			dayEnd.tm_sec = 59;
			time_t beginStamp = Tools::GetTimeStamp(dayBegin);
			time_t endStamp = Tools::GetTimeStamp(dayEnd);
			pDays.emplace_back(std::make_pair(beginStamp, endStamp));
		}
		// 首位的时间调整下
		std::pair<time_t, time_t> &frontTime = pDays.front();
		frontTime.first = pBeginTime;
		std::pair<time_t, time_t> &backTime = pDays.back();
		backTime.second = pEndTime;
		outDays.insert(outDays.begin(), pDays.begin(), pDays.end());
	}
}

std::string CTPTm::GetMarketDayStr(time_t pTime)
{
	tm pDateTime = Tools::GetDateTimeFrmStamp(pTime);
	
	std::string pdayStr = GetMarketDayStr(pDateTime);
	return pdayStr;
}

std::string CTPTm::GetMarketDayStr(tm pDateTime)
{
	tm mDay = GetMarketDay(pDateTime);

	std::string pdayStr = Tools::DateToStr(mDay);
	return pdayStr;
}

tm CTPTm::GetMarketDay(tm pDateTime)
{
	int curSectionTag = DateTimeSectionTag(pDateTime);
	bool isCurDay = true;
	if (curSectionTag == MarketDateTime_Evening)
	{
		if (pDateTime.tm_hour < 8)
		{
			isCurDay = false;
		}
	}
	if (isCurDay)
	{
		pDateTime.tm_hour = 0;
		pDateTime.tm_min = 0;
		pDateTime.tm_sec = 0;
		return pDateTime;
	}
	else
	{
		tm nDtime = Tools::AddDays(pDateTime, -1);
		nDtime.tm_hour = 0;
		nDtime.tm_min = 0;
		nDtime.tm_sec = 0;
		return nDtime;
	}
}

void CTPTm::DateRangeToDays(time_t pBeginDay, time_t pEndDay, std::vector<time_t> &outDays)
{
	tm beginDateTimeStruct = Tools::GetDateTimeFrmStamp(pBeginDay);
	tm endDateTimeStruct = Tools::GetDateTimeFrmStamp(pEndDay);
	tm beginDateStruct = Tools::GetDate(beginDateTimeStruct);
	tm endDateStruct = Tools::GetDate(endDateTimeStruct);

	std::vector<time_t> pDays;
	
	tm firstDt = beginDateStruct;
	time_t vTmStamp = 0;
	while ((vTmStamp = Tools::GetTimeStamp(firstDt)) < pEndDay)
	{
		pDays.emplace_back(vTmStamp);
		firstDt = Tools::AddDays(firstDt, 1);
	}

	// 把最后1天也加进来
	time_t theLastDayTime = Tools::GetTimeStamp(endDateStruct);
	if (pDays.empty() || pDays.back() != theLastDayTime)
	{
		pDays.emplace_back(theLastDayTime);
	}
	outDays.swap(pDays);
}

tm CTPTm::GetWorkDayTimeBeforeDaysLen(tm curDateTime, int pDays)
{
	tm curDt = Tools::GetDate(curDateTime);
	int daysCount = 0;
	HolidayManager pManager;
	while (true)
	{
		curDt = Tools::AddDays(curDt, -1);
		time_t pStamp = Tools::GetTimeStamp(curDt);
		// 判断是否在假期中
		if (!pManager.IsMarketDay(pStamp))
		{
			continue;
		}
		++daysCount;
		if (daysCount >= pDays)
		{
			break;
		}
	}
	return Tools::AddSecs(curDt, 4 * 60 * 60);
}

void CTPTm::GetSegmentRange(const tm &pDateTime, tm &outBeginDt, tm &outEndDt)
{
	int curSectionTag = DateTimeSectionTag(pDateTime);
	outBeginDt = outEndDt = pDateTime;

	outBeginDt.tm_hour = 0;
	outBeginDt.tm_min = 0;
	outBeginDt.tm_sec = 0;
	outEndDt.tm_hour = 0;
	outEndDt.tm_min = 0;
	outEndDt.tm_sec = 0;
	switch (curSectionTag)
	{
	case MarketDateTime_Morning:
		outBeginDt.tm_hour = MarketDateTime_Morning_BeginHour;
		outEndDt.tm_hour = MarketDateTime_Morning_EndHour;
		break;
	case MarketDateTime_Afternoon:
		outBeginDt.tm_hour = MarketDateTime_Afternoon_BeginHour;
		outEndDt.tm_hour = MarketDateTime_Afternoon_EndHour;
		break;
	case MarketDateTime_Evening:
	{
		if (pDateTime.tm_hour > 20) // 这是前半夜
		{
			outBeginDt.tm_hour = MarketDateTime_Evening_BeginHour;
			// 结束时间是第二天
			outEndDt = Tools::AddDays(outEndDt, 1);
			outEndDt.tm_hour = MarketDateTime_Evening_EndHour;
		}
		else // 这是后半夜
		{
			outBeginDt = Tools::AddDays(outBeginDt, -1);
			outBeginDt.tm_hour = MarketDateTime_Evening_BeginHour;
			outEndDt.tm_hour = MarketDateTime_Evening_EndHour;
		}
		
	}
	break;
	default:
		break;
	}
}

void CTPTm::GetSegmentMarketRange(const tm &pDateTime, tm &outBeginDt, tm &outEndDt)
{
	int curSectionTag = DateTimeSectionTag(pDateTime);
	outBeginDt = outEndDt = pDateTime;
	outBeginDt.tm_hour = 0;
	outBeginDt.tm_min = 0;
	outBeginDt.tm_sec = 0;
	outEndDt.tm_hour = 0;
	outEndDt.tm_min = 0;
	outEndDt.tm_sec = 0;
	switch (curSectionTag)
	{
	case MarketDateTime_Morning:
		outBeginDt.tm_hour = 8;
		outBeginDt.tm_min = 57;
		outEndDt.tm_hour = 12;
		break;
	case MarketDateTime_Afternoon:
		outBeginDt.tm_hour = 13;
		outEndDt.tm_hour = 15;
		break;
	case MarketDateTime_Evening:
	{
		if (pDateTime.tm_hour > 20) // 这是前半夜
		{
			outBeginDt.tm_hour = 21;
			// 结束时间是第二天
			outEndDt = Tools::AddDays(outEndDt, 1);
			outEndDt.tm_hour = 4;
		}
		else // 这是后半夜
		{
			outBeginDt = Tools::AddDays(outBeginDt, -1);
			outBeginDt.tm_hour = 21;
			outEndDt.tm_hour = 4;
		}
		
	}
	break;
	default:
		break;
	}
}

void CTPTm::GetPreviousSegmentMarketRange(const tm &pDateTime, tm &outBeginDt, tm &outEndDt)
{
	int curSectionTag = DateTimeSectionTag(pDateTime);
	outBeginDt = outEndDt = pDateTime;
	outBeginDt.tm_hour = 0;
	outBeginDt.tm_min = 0;
	outBeginDt.tm_sec = 0;
	outEndDt.tm_hour = 0;
	outEndDt.tm_min = 0;
	outEndDt.tm_sec = 0;
	switch (curSectionTag)
	{
	case MarketDateTime_Morning: // 返回前一天晚上的盘
		{
			outBeginDt = Tools::AddDays(outBeginDt, -1);
			outBeginDt.tm_hour = 21;
			outEndDt.tm_hour = 4;
		}
		break;
	case MarketDateTime_Afternoon: // 返回上午盘
		outBeginDt.tm_hour = 8;
		outBeginDt.tm_min = 57;
		outEndDt.tm_hour = 12;
		break;
	case MarketDateTime_Evening: // 返回下午盘
		outBeginDt.tm_hour = 13;
		outEndDt.tm_hour = 15;
	break;
	default:
		break;
	}
}

void CTPTm::GetNextSegmentMarketRange(const tm &pDateTime, tm &outBeginDt, tm &outEndDt)
{
	int curSectionTag = DateTimeSectionTag(pDateTime);
	outBeginDt = outEndDt = pDateTime;
	outBeginDt.tm_hour = 0;
	outBeginDt.tm_min = 0;
	outBeginDt.tm_sec = 0;
	outEndDt.tm_hour = 0;
	outEndDt.tm_min = 0;
	outEndDt.tm_sec = 0;
	switch (curSectionTag)
	{
	case MarketDateTime_Morning: // 返回下午盘
		outBeginDt.tm_hour = 13;
		outEndDt.tm_hour = 15;
		break;
	case MarketDateTime_Afternoon: // 返回夜盘
	{
		outBeginDt.tm_hour = 21;
		// 结束时间是第二天
		outEndDt = Tools::AddDays(outEndDt, 1);
		outEndDt.tm_hour = 4;
	}
		break;
	case MarketDateTime_Evening: // 返回第二天的上午盘
	{
		if (pDateTime.tm_hour > 20) // 这是前半夜
		{
			outBeginDt = Tools::AddDays(outBeginDt, 1);
			outBeginDt.tm_hour = 8;
			outBeginDt.tm_min = 57;
			outEndDt = Tools::AddDays(outEndDt, 1);
			outEndDt.tm_hour = 12;
		}
		else // 这是后半夜
		{
			outBeginDt.tm_hour = 8;
			outBeginDt.tm_min = 57;
			outEndDt.tm_hour = 12;
		}
	}
	break;
	default:
		break;
	}
}

int CTPTm::DateTimeSectionTag(const tm& tm_)
{
	if (tm_.tm_hour >= MarketDateTime_Morning_BeginHour && tm_.tm_hour < MarketDateTime_Morning_EndHour)
	{
		return MarketDateTime_Morning; // 当前属于上午行情段
	}
	if (tm_.tm_hour >= MarketDateTime_Afternoon_BeginHour && tm_.tm_hour < MarketDateTime_Afternoon_EndHour)
	{
		return MarketDateTime_Afternoon; // 当前属于下午行情段
	}
	if (tm_.tm_hour >= NoMarketDateTime_Afternoon_BeginHour && tm_.tm_hour < NoMarketDateTime_Afternoon_EndHour)
	{
		return NoMarketDateTime_Afternoon; // 当前属于下午非行情段
	}
	if (tm_.tm_hour >= NoMarketDateTime_Evening_BeginHour && tm_.tm_hour < NoMarketDateTime_Evening_EndHour)
	{
		return NoMarketDateTime_Evening; // 当前属于晚上非行情段
	}

	return MarketDateTime_Evening; // 当前属于晚上行情段
}

bool CTPTm::DayRange::operator<(const struct DayRange & right) const
{
	return (RangeBegin + RangeEnd) < (right.RangeBegin + right.RangeEnd);
}

CTPTm::HolidayManager::HolidayManager()
{
	initHolidayList();
}

void CTPTm::HolidayManager::SaveHolidayAndSpecialDay(const std::set<DayRange> &pHoliday, const std::set<DayRange> &pSpecialWorkDay)
{
	m_holidayList.clear();
	m_holidayList.insert(pHoliday.begin(), pHoliday.end());

	m_specialWorkDayList.clear();
	m_specialWorkDayList.insert(pSpecialWorkDay.begin(), pSpecialWorkDay.end());

	std::string optDir = OptionManager::Instance().GetGenDir() + "/Option";
	OptionManager::Instance().MakeSureDir(optDir);
	std::string holidayPath = optDir + "/HolidayRange.txt";

	QFile file(QString::fromLocal8Bit(holidayPath.c_str()));
	if (file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
	{
		QTextStream pout(&file);  //创建写入流
		if (!m_holidayList.empty())
		{
			for (std::set<DayRange>::iterator it = m_holidayList.begin(); it != m_holidayList.end(); ++it)
			{
				QString pHolidayStr = dayRangeToStr('h', *it);
				pout << pHolidayStr << endl;
			}
		}
		if (!m_specialWorkDayList.empty())
		{
			for (std::set<DayRange>::iterator it = m_specialWorkDayList.begin(); it != m_specialWorkDayList.end(); ++it)
			{
				QString pHolidayStr = dayRangeToStr('w', *it);
				pout << pHolidayStr << endl;
			}
		}
		file.close();
	}
}

bool CTPTm::HolidayManager::IsMarketTime(const time_t &pDateTime)
{
	// 非行情日
	if (!IsMarketDay(pDateTime))
	{
		return false;
	}
	// 行情日判断非行情时间段
	tm tm_ = Tools::GetDateTimeFrmStamp(pDateTime);
	// 判断是否在非行情时间段中
	int segTag = CTPTm::DateTimeSectionTag(tm_);
	if (segTag == 4 || segTag == 5)
	{
		return false;
	}
	return true;
}

bool CTPTm::HolidayManager::IsMarketDay(const time_t &pDateTime)
{
	// 在法定节日中
	if (isInSpecialHoliday(pDateTime))
	{
		return false;
	}
	// 判断正常工作日
	if (!isInWeekend(pDateTime))
	{
		return true;
	}
	// 判断周末是否为工作日
	for (std::set<DayRange>::iterator it = m_specialWorkDayList.begin(); it != m_specialWorkDayList.end(); ++it)
	{
		if (pDateTime > it->RangeBegin && pDateTime < it->RangeEnd)
		{
			return true;
		}
	}
	return false;
}

void CTPTm::HolidayManager::initHolidayList()
{
	std::string optDir = OptionManager::Instance().GetGenDir() + "/Option";
	OptionManager::Instance().MakeSureDir(optDir);
	std::string holidayPath = optDir + "/HolidayRange.txt";

	QFile file(holidayPath.c_str());
	if (file.open(QIODevice::ReadOnly | QIODevice::Text))
	{
		while (!file.atEnd())
		{
			QByteArray line = file.readLine();
			QString str(line);
			DayRange pRange;
			DayType datType = getDayRange(str, pRange);
			if (datType == Holiday)
			{
				m_holidayList.insert(pRange);
			}
			else if (datType == WorkDay)
			{
				m_specialWorkDayList.insert(pRange);
			}
		}
		file.close();
	}
}

CTPTm::HolidayManager::DayType CTPTm::HolidayManager::getDayRange(const QString &pStr, DayRange &outRange)
{
	QStringList dayContentList = pStr.split(';');
	if (dayContentList.size() != 3)
	{
		return Unknown;
	}
	// 判断假期或工作日
	DayType pDayType = Unknown;
	QString dayTypeStr = dayContentList.at(0);
	if (dayTypeStr.compare("h") == 0)
	{
		pDayType = Holiday;
	}
	else if (dayTypeStr.compare("w") == 0)
	{
		pDayType = WorkDay;
	}
	else
	{
		return Unknown;
	}
	QString beginDayStr = dayContentList.at(1);
	tm holidayBeginDt = Tools::DateStr2TM(beginDayStr.toLocal8Bit().data());
	holidayBeginDt.tm_hour = 4;
	outRange.RangeBegin = Tools::GetTimeStamp(holidayBeginDt); //已经减了8个时区

	QString endDayStr = dayContentList.at(2);
	tm holidayEndDt = Tools::DateStr2TM(endDayStr.toLocal8Bit().data());
	holidayEndDt = Tools::AddDays(holidayEndDt, 1);
	holidayEndDt.tm_hour = 4;
	outRange.RangeEnd = Tools::GetTimeStamp(holidayEndDt); //已经减了8个时区

	return pDayType;
}

QString CTPTm::HolidayManager::dayRangeToStr(char pTag, const DayRange &pRange)
{
	QStringList pRangeList;
	pRangeList << QString(pTag);

	tm rangeBeginTm = Tools::GetDateTimeFrmStamp(pRange.RangeBegin);
	std::string rangeBeginTmStr = Tools::DateToStr(rangeBeginTm);
	pRangeList << QString::fromLocal8Bit(rangeBeginTmStr.c_str());

	tm rangeEndTm = Tools::GetDateTimeFrmStamp(pRange.RangeEnd);
	std::string rangeEndTmStr = Tools::DateToStr(rangeEndTm);
	pRangeList << QString::fromLocal8Bit(rangeEndTmStr.c_str());

	return	pRangeList.join(';');
}

bool CTPTm::HolidayManager::isInSpecialHoliday(const time_t &pDateTime)
{
	// 遍历所有的指定节日
	for (std::set<DayRange>::iterator it = m_holidayList.begin(); it != m_holidayList.end(); ++it)
	{
		if (pDateTime > it->RangeBegin && pDateTime < it->RangeEnd)
		{
			return true;
		}
	}
	return false;
}

bool CTPTm::HolidayManager::isInWeekend(const time_t &pDateTime)
{
	tm dtTM = Tools::GetDateTimeFrmStamp(pDateTime);
	if (dtTM.tm_wday == 0) // 周日
	{
		return true;
	}
	if (dtTM.tm_wday == 6) // 周六
	{
		if (dtTM.tm_hour >= 4) // 周六从4点开始过
		{
			return true;
		}
	}
	return false;
}
